
//DNS Query Program on Linux
//Author : Silver Moon (m00n.silv3r@gmail.com)
//Dated : 29/4/2009
//http://www.binarytides.com/dns-query-code-in-c-with-linux-sockets/

//Header Files
#include <stdio.h>	//printf
#include <string.h>	//strlen
#include <stdlib.h>	//malloc
#include <sys/socket.h>	//you know what this is for
#include <arpa/inet.h>	//inet_addr , inet_ntoa , ntohs etc
#include <netinet/in.h>
#include <unistd.h>	//getpid

//Types of DNS resource records :)
#define T_A 1 //Ipv4 address
#define T_NS 2 //Nameserver
#define T_CNAME 5 // canonical name
#define T_SOA 6 /* start of authority zone */
#define T_PTR 12 /* domain name pointer */
#define T_MX 15 //Mail server

//Function Prototypes
void ngethostbyname (struct in_addr *, unsigned char* , int);
void ChangetoDnsNameFormat (unsigned char*,unsigned char*);
unsigned char* ReadName (unsigned char*,unsigned char*,int*);
void get_dns_servers();

#pragma pack(push, 1)
//DNS header structure
struct DNS_HEADER
{
  uint16_t id; // identification number

  unsigned char rd :1; // recursion desired
  unsigned char tc :1; // truncated message
  unsigned char aa :1; // authoritive answer
  unsigned char opcode :4; // purpose of message
  unsigned char qr :1; // query/response flag

  unsigned char rcode :4; // response code
  unsigned char cd :1; // checking disabled
  unsigned char ad :1; // authenticated data
  unsigned char z :1; // its z! reserved
  unsigned char ra :1; // recursion available

  uint16_t q_count; // number of question entries
  uint16_t ans_count; // number of answer entries
  uint16_t auth_count; // number of authority entries
  uint16_t add_count; // number of resource entries
};

//Constant sized fields of query structure
struct QUESTION
{
  uint16_t qtype;
  uint16_t qclass;
};

//Constant sized fields of the resource record structure
struct R_DATA
{
  uint16_t type;
  uint16_t _class;
  uint32_t ttl;
  uint16_t data_len;
};
#pragma pack(pop)

//Pointers to resource record contents
struct RES_RECORD
{
  unsigned char *name;
  struct R_DATA *resource;
  unsigned char *rdata;
};

//Structure of a Query
typedef struct
{
  unsigned char *name;
  struct QUESTION *ques;
} QUERY;

int main( int argc , char *argv[])
{
  struct in_addr dns_addr;

  if (argc != 3)
    {
      printf("Usage: mini-host hostname dns-server-ip\n");
      return 0;
    }

  if (inet_pton(AF_INET, argv[2], &dns_addr) != 1)
    {
      printf("Usage: mini-host hostname dns-server-ip\n");
      return 0;
    }

  ngethostbyname(&dns_addr, (unsigned char *)argv[1] , T_A);

  return 0;
}

/*
 * Perform a DNS query by sending a packet
 * */
void ngethostbyname(struct in_addr *dns_addr, unsigned char *host , int query_type)
{
  unsigned char buf[65536],*qname,*reader;
  int i , j , stop , s;

  struct sockaddr_in a;

  struct RES_RECORD answers[20],auth[20],addit[20]; //the replies from the DNS server
  struct sockaddr_in dest;

  struct DNS_HEADER *dns = NULL;
  struct QUESTION *qinfo = NULL;

  printf("Resolving %s" , host);

  s = socket(AF_INET , SOCK_DGRAM , IPPROTO_UDP); //UDP packet for DNS queries

  dest.sin_family = AF_INET;
  dest.sin_port = htons(53);
  dest.sin_addr.s_addr = dns_addr->s_addr;

  //Set the DNS structure to standard queries
  dns = (struct DNS_HEADER *)&buf;

  dns->id = (uint16_t) htons(getpid());
  dns->qr = 0; //This is a query
  dns->opcode = 0; //This is a standard query
  dns->aa = 0; //Not Authoritative
  dns->tc = 0; //This message is not truncated
  dns->rd = 1; //Recursion Desired
  dns->ra = 0; //Recursion not available! hey we dont have it (lol)
  dns->z = 0;
  dns->ad = 0;
  dns->cd = 0;
  dns->rcode = 0;
  dns->q_count = htons(1); //we have only 1 question
  dns->ans_count = 0;
  dns->auth_count = 0;
  dns->add_count = 0;

  //point to the query portion
  qname =(unsigned char*)&buf[sizeof(struct DNS_HEADER)];

  ChangetoDnsNameFormat(qname , host);
  qinfo =(struct QUESTION*)&buf[sizeof(struct DNS_HEADER) + (strlen((const char*)qname) + 1)]; //fill it

  qinfo->qtype = htons( query_type ); //type of the query , A , MX , CNAME , NS etc
  qinfo->qclass = htons(1); //its internet (lol)

  printf("\nSending Packet...");
  if( sendto(s,(char*)buf,sizeof(struct DNS_HEADER) + (strlen((const char*)qname)+1) + sizeof(struct QUESTION),0,(struct sockaddr*)&dest,sizeof(dest)) < 0)
    {
      perror("sendto failed");
    }
  printf("Done");

  //Receive the answer
  i = sizeof dest;
  printf("\nReceiving answer...");
  if(recvfrom (s,(char*)buf , 65536 , 0 , (struct sockaddr*)&dest , (socklen_t*)&i ) < 0)
    {
      perror("recvfrom failed");
    }
  printf("Done");

  dns = (struct DNS_HEADER*) buf;

  //move ahead of the dns header and the query field
  reader = &buf[sizeof(struct DNS_HEADER) + (strlen((const char*)qname)+1) + sizeof(struct QUESTION)];

  printf("\nThe response contains : ");
  printf("\n %d Questions.",ntohs(dns->q_count));
  printf("\n %d Answers.",ntohs(dns->ans_count));
  printf("\n %d Authoritative Servers.",ntohs(dns->auth_count));
  printf("\n %d Additional records.\n\n",ntohs(dns->add_count));

  //Start reading answers
  stop=0;

  for(i=0;i<ntohs(dns->ans_count);i++)
    {
      answers[i].name=ReadName(reader,buf,&stop);
      reader = reader + stop;

      answers[i].resource = (struct R_DATA*)(reader);
      reader = reader + sizeof(struct R_DATA);

      if(ntohs(answers[i].resource->type) == 1) //if its an ipv4 address
	{
	  answers[i].rdata = (unsigned char*)malloc(ntohs(answers[i].resource->data_len));

	  for(j=0 ; j<ntohs(answers[i].resource->data_len) ; j++)
	    {
	      answers[i].rdata[j]=reader[j];
	    }

	  answers[i].rdata[ntohs(answers[i].resource->data_len)] = '\0';

	  reader = reader + ntohs(answers[i].resource->data_len);
	}
      else
	{
	  answers[i].rdata = ReadName(reader,buf,&stop);
	  reader = reader + stop;
	}
    }

  //read authorities
  for(i=0;i<ntohs(dns->auth_count);i++)
    {
      auth[i].name=ReadName(reader,buf,&stop);
      reader+=stop;

      auth[i].resource=(struct R_DATA*)(reader);
      reader+=sizeof(struct R_DATA);

      auth[i].rdata=ReadName(reader,buf,&stop);
      reader+=stop;
    }

  //read additional
  for(i=0;i<ntohs(dns->add_count);i++)
    {
      addit[i].name=ReadName(reader,buf,&stop);
      reader+=stop;

      addit[i].resource=(struct R_DATA*)(reader);
      reader+=sizeof(struct R_DATA);

      if(ntohs(addit[i].resource->type)==1)
	{
	  addit[i].rdata = (unsigned char*)malloc(ntohs(addit[i].resource->data_len));
	  for(j=0;j<ntohs(addit[i].resource->data_len);j++)
	    addit[i].rdata[j]=reader[j];

	  addit[i].rdata[ntohs(addit[i].resource->data_len)]='\0';
	  reader+=ntohs(addit[i].resource->data_len);
	}
      else
	{
	  addit[i].rdata=ReadName(reader,buf,&stop);
	  reader+=stop;
	}
    }

  //print answers
  printf("\nAnswer Records : %d \n" , ntohs(dns->ans_count) );
  for(i=0 ; i < ntohs(dns->ans_count) ; i++)
    {
      printf("Name : %s ",answers[i].name);

      if( ntohs(answers[i].resource->type) == T_A) //IPv4 address
	{
	  long *p;
	  p=(long*)answers[i].rdata;
	  a.sin_addr.s_addr=(*p); //working without ntohl
	  printf("has IPv4 address : %s",inet_ntoa(a.sin_addr));
	}

      if(ntohs(answers[i].resource->type)==5) 
	{
	  //Canonical name for an alias
	  printf("has alias name : %s",answers[i].rdata);
	}

      printf("\n");
    }

  //print authorities
  printf("\nAuthoritive Records : %d \n" , ntohs(dns->auth_count) );
  for( i=0 ; i < ntohs(dns->auth_count) ; i++)
    {

      printf("Name : %s ",auth[i].name);
      if(ntohs(auth[i].resource->type)==2)
	{
	  printf("has nameserver : %s",auth[i].rdata);
	}
      printf("\n");
    }

  //print additional resource records
  printf("\nAdditional Records : %d \n" , ntohs(dns->add_count) );
  for(i=0; i < ntohs(dns->add_count) ; i++)
    {
      printf("Name : %s ",addit[i].name);
      if(ntohs(addit[i].resource->type)==1)
	{
	  long *p;
	  p=(long*)addit[i].rdata;
	  a.sin_addr.s_addr=(*p);
	  printf("has IPv4 address : %s",inet_ntoa(a.sin_addr));
	}
      printf("\n");
    }
  return;
}

/*
 * 
 * */
u_char* ReadName(unsigned char* reader,unsigned char* buffer,int* count)
{
  unsigned char *name;
  unsigned int p=0,jumped=0,offset;
  int i , j;

  *count = 1;
  name = (unsigned char*)malloc(256);

  name[0]='\0';

  //read the names in 3www6google3com format
  while(*reader!=0)
    {
      if(*reader>=192)
	{
	  offset = (*reader)*256 + *(reader+1) - 49152; //49152 = 11000000 00000000 ;)
	  reader = buffer + offset - 1;
	  jumped = 1; //we have jumped to another location so counting wont go up!
	}
      else
	{
	  name[p++]=*reader;
	}

      reader = reader+1;

      if(jumped==0)
	{
	  *count = *count + 1; //if we havent jumped to another location then we can count up
	}
    }

  name[p]='\0'; //string complete
  if(jumped==1)
    {
      *count = *count + 1; //number of steps we actually moved forward in the packet
    }

  //now convert 3www6google3com0 to www.google.com
  for(i=0;i<(int)strlen((const char*)name);i++) 
    {
      p=name[i];
      for(j=0;j<(int)p;j++) 
	{
	  name[i]=name[i+1];
	  i=i+1;
	}
      name[i]='.';
    }
  name[i-1]='\0'; //remove the last dot
  return name;
}

/*
 * This will convert www.google.com to 3www6google3com 
 * got it :)
 * */
void ChangetoDnsNameFormat(unsigned char* dns,unsigned char* host) 
{
  int lock = 0 , i;
  strcat((char*)host,".");

  for(i = 0 ; i < strlen((char*)host) ; i++) 
    {
      if(host[i]=='.') 
	{
	  *dns++ = i-lock;
	  for(;lock<i;lock++) 
	    {
	      *dns++=host[lock];
	    }
	  lock++; //or lock=i+1;
	}
    }
  *dns++='\0';
}


